package com.fattree.apachepoi.word.utils;

import com.fattree.apachepoi.word.bean.TParam;
import com.fattree.apachepoi.word.bean.TParamType;
import com.fattree.apachepoi.word.bean.Watermark;
import com.microsoft.schemas.vml.*;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlObject;

import javax.xml.namespace.QName;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 肥树 wangjiaxiu248@163.com
 * @Package com.fattree.apachepoi.word.utils
 * @Description: TODO ( for docx of word )
 * @Date 2022/1/24 5:59 PM
 * @Version V1.0
 */
public class ApachePOIUtils {

    private static int BUFFER_SIZE = 1024;

    /**
     * 水印图形样式匹配
     */
    private static Pattern CTSHAPE_STYLE_PATTERN = Pattern.compile(";");

    /**
     * 水印层级（貌似没有启作用）
     */
    private static String WATERMARK_Z_INDEX = "999999999";

    /**
     * 模板参数名称匹配
     */
    private static String PARAM_NAME_REGEX = "(list#[a-zA-Z0-9_]+\\.)?((a|t|img)#)?(\\$)?([a-zA-Z0-9_])+";
    /**
     * 模板参数引用开始符号
     */
    private static String PARAM_START_QUOTE = "\\{\\{\\{";
    /**
     * 模板参数引用结束符号
     */
    private static String PARAM_END_QUOTE = "}}}";

    /**
     * word 模板中参数匹配
     * a# 超链接
     * t# 文本，可忽略
     * img# 图片
     * list# 列表
     * $ 系统参数
     */
    private static Pattern PARAM_NAME_PATTERN =
            Pattern.compile(PARAM_START_QUOTE + PARAM_NAME_REGEX + PARAM_END_QUOTE, Pattern.CASE_INSENSITIVE);

    /**
     * word 模板参数引用符号匹配
     */
    private static String QUOTE_REGEX = "(" + PARAM_START_QUOTE + ")|(" + PARAM_END_QUOTE + ")";

    /**
     * 模板参数包含的特殊含义的符号
     */
    private static String PARAM_QUOTE_REGEX = "#|\\.|\\$";

    /**
     * 类型分隔符
     */
    private static String PARAM_TYPE_SPLIT = "#";
    /**
     * 属性分隔符
     */
    private static String PARAM_ATTR_SPLIT = ".";
    /**
     * 系统变量分隔符
     */
    private static String PARAM_SYSTERM = "$";

    /**
     * 读取本地文件
     *
     * @param fileName
     * @return
     * @throws Exception
     */
    public static XWPFDocument getDocument(String fileName) throws Exception {
        File docFile = new File(fileName);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        FileInputStream fis = new FileInputStream(docFile);
        int len = -1;
        byte[] buffer = new byte[BUFFER_SIZE];
        while ((len = fis.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        fis.close();
        bos.close();
        InputStream bis = new ByteArrayInputStream(bos.toByteArray());
        XWPFDocument document = new XWPFDocument(bis);
        return document;
    }

    /**
     * 根据字节数组获取 document
     *
     * @param bytes
     * @return
     * @throws Exception
     */
    public static XWPFDocument getDocument(byte[] bytes) throws Exception {
        InputStream bis = new ByteArrayInputStream(bytes);
        XWPFDocument document = new XWPFDocument(bis);
        return document;
    }

    /**
     * 根据二进制流获取 document
     *
     * @param is
     * @return
     * @throws Exception
     */
    public static XWPFDocument getDocument(InputStream is) throws Exception {
        XWPFDocument document = new XWPFDocument(is);
        return document;
    }

    /**
     * 获取 word 中所有的段落（paragraph，table）
     * 替换的内容有：文本、图片、超链接、列表
     *
     * @param document
     * @return
     */
    public static List<XWPFParagraph> getParagraphs(XWPFDocument document) {
        List<XWPFParagraph> paragraphList = new ArrayList<>();
        List<IBodyElement> iBodyElementList = document.getBodyElements();
        for (IBodyElement bodyElement : iBodyElementList) {
            BodyElementType bodyElementType = bodyElement.getElementType();
            switch (bodyElementType) {
                case CONTENTCONTROL:
                    break;
                case PARAGRAPH:
                    XWPFParagraph paragraph = (XWPFParagraph) bodyElement;
                    paragraphList.add(paragraph);
                    break;
                case TABLE:
                    XWPFTable table = (XWPFTable) bodyElement;
                    int rowsNum = table.getNumberOfRows();
                    for (int i = 0; i < rowsNum; i++) {
                        XWPFTableRow row = table.getRow(i);
                        for (XWPFTableCell cell : row.getTableCells()) {
                            paragraphList.addAll(paragraphList.size(), cell.getParagraphs());
                        }
                    }
                    break;
                default:
                    break;
            }
        }
        return paragraphList;
    }

    public static void writeDocument(XWPFDocument document, String newFileName) throws Exception {
        OutputStream os = new FileOutputStream(newFileName);
        document.write(os);
        os.close();
    }

    /**
     * 给 docx 添加水印
     *
     * @param document  word 文档
     * @param watermark 水印
     */
    public static void addWatermark(XWPFDocument document, Watermark watermark) {
        if (watermark != null && ConsistentUtils.INT_ONE == watermark.getType()) {
            XWPFHeaderFooterPolicy headerFooterPolicy = document.getHeaderFooterPolicy();
            if (headerFooterPolicy == null) {
                headerFooterPolicy = document.createHeaderFooterPolicy();
            }
            headerFooterPolicy.createWatermark(watermark.getValue());
            XWPFHeader defaultHeader = headerFooterPolicy.getHeader(XWPFHeaderFooterPolicy.DEFAULT);
            XWPFHeader evenHeader = headerFooterPolicy.getHeader(XWPFHeaderFooterPolicy.EVEN);
            addWatermark(defaultHeader.getParagraphArray(0), watermark);
            addWatermark(evenHeader.getParagraphArray(0), watermark);
        }
    }

    public static void replaceTextForParagraphNew(XWPFParagraph paragraph, Map<String, Object> dataMap) {
        String paraGraphText = paragraph.getText();
        Matcher matcher = PARAM_NAME_PATTERN.matcher(paraGraphText);
        Map<String, Object> paramMap = new HashMap<>();
        while (matcher.find()) {
            String paramKey = (matcher.group(0)).replaceAll(QUOTE_REGEX, "");
            if (paramMap.get(paramKey) != null) {
                continue;
            }
            System.out.println("matcher.group(0)=" + matcher.group(0) + ",paramKey=" + paramKey);
            paramParser(paramKey, paramMap);
        }
    }

    /**
     * 模板参数迭代解析
     *
     * @param paramKey
     * @param paramMap
     */
    private static void paramParser(String paramKey, Map<String, Object> paramMap) {
        TParam tParam = new TParam();
        if (paramKey.matches(PARAM_QUOTE_REGEX)) {
            if (!paramKey.contains(PARAM_TYPE_SPLIT) && !paramKey.contains(PARAM_TYPE_SPLIT)) {
            } else {
            }
            //获取类型
            //获取参数名称
            paramParser(paramKey, paramMap);
        } else {
            tParam.setParamName(paramKey);
            tParam.setParamType(TParamType.DEFAULT);
            paramMap.put(paramKey, tParam);
        }

        return;
    }

    /**
     * 替换 word 模板段落中的参数
     *
     * @param paragraph 文档段落，包括表格中的段落
     * @param dataMap   需要替换的数据
     */
    public static void replaceTextForParagraph(XWPFParagraph paragraph, Map<String, Object> dataMap) {
        List<XWPFRun> runList = paragraph.getRuns();
        for (int i = 0; i < runList.size(); i++) {
            XWPFRun run = runList.get(i);
            int fontSize = run.getFontSize();
            String fontFamily = run.getFontFamily();
            String color = run.getColor();
            String style = run.getStyle();
            String runText = run.text();
            Matcher matcher = PARAM_NAME_PATTERN.matcher(runText);
            Map<String, String> keyMap = new HashMap<>();
            while (matcher.find()) {
                int cnt = matcher.groupCount();
                for (int j = 0; j < cnt; j++) {
                    if (matcher.group(j) != null && matcher.group(j).matches(PARAM_NAME_REGEX)) {
                        String paramKey = (matcher.group(j)).replaceAll(QUOTE_REGEX, "");
                        if (keyMap.get(paramKey) != null) {
                            continue;
                        }
                        keyMap.put(paramKey, paramKey);
                        if (dataMap.get(paramKey) != null) {
                            String paramValue = (String) dataMap.get(paramKey);
                            if (paramValue.contains("\n")) {
                                String[] runValues = paramValue.split("\n");
                                for (int k = 0; k < runValues.length; k++) {
                                    String runVal = runValues[k];
                                    //如果是第一个 run，先移除掉，再新增
                                    if (k == 0) {
                                        paragraph.removeRun(i);
                                    }
                                    XWPFRun insertNewRun = paragraph.createRun();
                                    insertNewRun.setFontSize(fontSize);
                                    insertNewRun.setFontFamily(fontFamily);
                                    insertNewRun.setColor(color);
                                    insertNewRun.setStyle(style);
                                    runVal = replaceText(runText, paramKey, runVal);
                                    runVal = runVal.replaceAll(QUOTE_REGEX, "");
                                    insertNewRun.setText(runVal);
                                    //文本换行
                                    //insertNewRun.addCarriageReturn();
                                    insertNewRun.addBreak();
                                }
                            } else {
                                run.setText(replaceText(runText, paramKey, paramValue), 0);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 将模板原始内容中的模板参数进行替换
     *
     * @param plainText   原始文本
     * @param paramName   模板参数
     * @param replacement 替换内容
     * @return
     */
    private static String replaceText(String plainText, String paramName, String replacement) {
        return plainText.replaceAll(PARAM_START_QUOTE + paramName + PARAM_END_QUOTE, replacement);
    }

    /**
     * 添加水印的实现方法
     *
     * @param paragraph 页眉段落
     * @param watermark 水印
     */
    private static void addWatermark(XWPFParagraph paragraph, Watermark watermark) {
        XmlObject[] xmlobjects = paragraph.getCTP().getRArray(0).getPictArray(0).selectChildren(
                new QName("urn:schemas-microsoft-com:vml", "shape"));
        if (xmlobjects != null && xmlobjects.length > 0) {
            CTShape ctshape = (CTShape) xmlobjects[0];
            // set fill color
            ctshape.setFillcolor(watermark.getFillColor());
            // set rotation
            ctshape.setStyle(resetCTShapeStyle(ctshape.getStyle(), watermark));
        }
    }

    /**
     * 修改默认水印的样式
     *
     * @param plainStyle 原始样式
     * @param watermark  水印
     * @return
     */
    private static String resetCTShapeStyle(String plainStyle, Watermark watermark) {
        String shapeStyle = plainStyle;
        String[] styleArray = CTSHAPE_STYLE_PATTERN.split(plainStyle);
        for (String style : styleArray) {
            String newStyle = "";
            if (style.startsWith("height:")) {
                newStyle = "height:" + watermark.getFontSize() + watermark.getUnit();
                shapeStyle = shapeStyle.replace(style, newStyle);
            }
            if (style.startsWith("width:")) {
                //单个水印宽度为：水印字数 * (字体大小+间距)
                newStyle = "width:" +
                        (
                                (watermark.getValue().length() - 1) * (watermark.getFontSize() + watermark.getHorizontal())
                        ) + watermark.getUnit();
                shapeStyle = shapeStyle.replace(style, newStyle);
            }
            if (style.startsWith("z-index:")) {
                newStyle = "z-index:" + WATERMARK_Z_INDEX;
                shapeStyle = shapeStyle.replace(style, newStyle);
            }
        }
        shapeStyle = shapeStyle + ";rotation:" + watermark.getRotate();
        return shapeStyle;
    }
}